/*
 * Web Crawler
 * Date: November 01, 2011
 * Group JEDi
 * Author(s): James Lovato, Efim Todorasco, Daniel Garrison
 */
package multithread;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Observable;
import java.util.Observer;

/**
 * Formats reports based on the data gathered from a single page and writes
 * the aggregate data to a text file "spiderRunMT.txt"
 * 
 * @author James Lovato, Efim Todorasco, Daniel Garrison
 * @version Fall 2011
 */
public class ThreadSafeReporter extends Observable{
	/**
	 * The new line constant.
	 */
	private static final String NEW_LINE = "\n";
	
	/**
	 * A constant for 3 tabs.
	 */
	private static final String TAB_3 =  "\t\t\t";
	
	/**
	 * A constant for 2 tabs.
	 */
	private static final String TAB_2 =  "\t\t";

	/**
	 * A cutoff limit for determining the correct number of tab positions based on string length.
	 */
	private static final int TAB_CUTOFF = 8;
	
	/**
	 * Constant to convert nano time to millisecond time.
	 */
	private static final int NANO_TO_MILLI = 1000000;
	
	/**
	 * Constant to convert milliseconds to seconds
	 */
	private static final int MILLI_TO_SECONDS = 1000;
	
	/**
	 * Constant to convert seconds to minutes
	 */
	private static final int SECONDS_TO_MINUTES = 60;
	
	/**
	 * The number of pages to report in total.
	 */
	private int my_num_of_pages;
	
	/**
	 * An array of string values that where searched for. 
	 * Used to format output.
	 */
	private String[] my_words_to_count;
	/**
	 * Individual report for the page
	 */
	private static StringBuilder my_report = new StringBuilder();
	/**
	 * The final report
	 */
	private static StringBuilder my_final_report = new StringBuilder();
	/**
	 * Format for decimal numbers
	 */
	private DecimalFormat my_format = new DecimalFormat("#0.00");
	
	/**
	 * Constructor for the Reporter class.  This version is not Thread-Safe.
	 * @param the_num_of_pages the number of pages to report.
	 * @param the_words_to_count an array of search words.
	 */
	public ThreadSafeReporter(int the_num_of_pages, String[] the_words_to_count) {
		my_num_of_pages = the_num_of_pages;
		my_words_to_count = the_words_to_count;
	}
	
	/**
	 * Calling this method causes the data passed in to be printed to the console
	 * in a format as shown below.
	 * 
	 * <pre>
	 * Forms incoming data from a single report into a readable string output 
	 * and prints to the console in this form
	 * URL Parsed: http://www...
	 * Page Number: 98
	 * Word count: 13
	 * URL count: 10
	 * Average words per page: (total number of words / page number)
	 * Average URL per page: (total number of URLs / page number)
	 * Time to parse page: XXXX milliseconds
	 * Ave. page parse time: XXXX milliseconds
	 * Keyword				Total hits				Ave. Hits / Page
	 * Intelligence			6 / 128					1.31						
	 * artificial			0 / 68					0.69
	 * etc...
	 * </pre>
	 * 
	 * @param the_metrics a data metric object.
	 * @param the_total_words the total number of words on this page.
	 * @param the_total_links the total number of links on this page.
	 */
	public void reportPage(DataMetrics the_metrics, int the_total_words, int the_total_links, 
		final int[] the_word_count_totals, long the_total_parse_time) {
		final int current_page_number = the_metrics.pageNumber();
		final int[] word_counts = the_metrics.hitCount();
		my_report.append("URL Parsed: ");
		my_report.append(the_metrics.getURL());
		my_report.append(NEW_LINE);
		my_report.append("Page Number: ");
		my_report.append(current_page_number);
		my_report.append(NEW_LINE);
		my_report.append("Word count: ");
		my_report.append(the_metrics.wordCount());
		my_report.append(NEW_LINE);
		my_report.append("URL count: ");
		my_report.append(the_metrics.linkCount());
		my_report.append(NEW_LINE);
		my_report.append("Average words per page: ");
		my_report.append(the_total_words / current_page_number);
		my_report.append(NEW_LINE);
		my_report.append("Average URLs per page: ");
		my_report.append(the_total_links / current_page_number);
		my_report.append(NEW_LINE);
		my_report.append("Time to parse page: ");
		my_report.append((float) the_metrics.parseTime() / NANO_TO_MILLI);
		my_report.append(" milliseconds");
		my_report.append(NEW_LINE);		
		my_report.append("Ave time to parse: ");
		my_report.append((float) the_total_parse_time / current_page_number / NANO_TO_MILLI);
		my_report.append(" milliseconds");
		my_report.append(NEW_LINE);
		my_report.append("Keyword\t\tTotal hits\tAve. hit per page");
		my_report.append(NEW_LINE);
		for (int i = 0; i < my_words_to_count.length; i++) {
			my_report.append(my_words_to_count[i]);
			my_report.append(String.format("%1$#"+ (20 - my_words_to_count[i].length())+"s", ""));
			my_report.append(word_counts[i]);
			my_report.append(" / ");
			my_report.append(the_word_count_totals[i]);
			my_report.append(TAB_2);
			my_report.append(my_format.format((float) the_word_count_totals[i] / current_page_number));
			my_report.append(NEW_LINE);
		}
		my_report.append(NEW_LINE);
		System.out.println(my_report.toString());
		setChanged();
		notifyObservers(my_report.toString());
	}

	/**
	 * Prints a special Final report for the whole crawling process.
	 * <pre>
	 * Total number of pages crawled: 1000
	 * Total number of words: 8,000
	 * Total number of url links: 70,000
	 * Total time spent parsing: 9 minutes 39 seconds
	 * </pre>
	 * @param the_total_word_count the total number of words counted
	 * @param the_total_links the total number of links counted
	 * @param the_total_time the total amount of time running.
	 */
	public static String getReport() {
		return my_report.toString();
	}
	public static void reset() {
		my_report = new StringBuilder();
		my_final_report = new StringBuilder();
	}
	public void finalReport(int the_total_word_count, int the_total_links, long the_total_time,
		final int the_number_of_threads, long the_total_parse_time) {
		// int seconds = (int) (the_total_time / NANO_TO_MILLI / MILLI_TO_SECONDS);
		// int minutes = seconds / SECONDS_TO_MINUTES;
		
		my_final_report.append("--- Printing Final Report ---");
		my_final_report.append("\n");		
		my_final_report.append("Total number of pages crawled: ");
		my_final_report.append(my_num_of_pages);
		my_final_report.append(NEW_LINE);
		my_final_report.append("Thead count: ");
		my_final_report.append(the_number_of_threads);
		my_final_report.append(NEW_LINE);
		my_final_report.append("Total number of words: ");
		my_final_report.append(the_total_word_count);
		my_final_report.append(NEW_LINE);
		my_final_report.append("Total number of URL links: ");
		my_final_report.append(the_total_links);
		my_final_report.append(NEW_LINE);
		my_final_report.append("Total time spent running: ");
		my_final_report.append(my_format.format((float) the_total_time / NANO_TO_MILLI));
		my_final_report.append(" milliseconds ");
		my_final_report.append(NEW_LINE);
		my_final_report.append("Total time spent parsing: ");
		my_final_report.append(my_format.format((float) the_total_parse_time / NANO_TO_MILLI));
		my_final_report.append(" milliseconds ");
		my_final_report.append(NEW_LINE);		
		setChanged();
		my_report = my_report.append(my_final_report.toString());
		notifyObservers(my_report.toString());
		//textWriter(my_final_report.toString());
	}
	
	/**
	 * Writes all of the stored outputs to a text file.
	 * The current implementation uses a list of string compiled as the
	 * crawler runs and writes each String "entry" in to the file in one
	 * action so that the file is not opened an closed for each page crawled.
	 * 
	 * @param the_text the string to output to the file. 
	 */
	private void textWriter(String the_output) {
		Writer output = null;
	    File file = new File("spiderRunMT.txt");
		try {
			// to append I think we need to open "spiderRunMT.txt" instead of creating a new one.
			output = new BufferedWriter(new FileWriter(file, true));
			output.append(the_output + "\n");
		    output.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		System.out.println("Your file has been written");
	}
}
